package com.ruoyi.common.core.api.version;

import com.ruoyi.plus.constants.CommonConstants;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;

import javax.servlet.http.HttpServletRequest;

/**
 * API version condition
 *
 * @author likun
 */
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
    /**
     * 版本号格式举例:
     * 1
     * 1.0.0
     * 1.0.2
     * 1.0.3.0
     */
    private final static String VERSION_PREFIX_PATTERN = "([0-9]+\\.{0,1}[0-9]{0,4}\\.{0,1}[0-9]{0,4}\\.{0,1}[0-9]{0,4})";
    /**
     * API VERSION interface
     **/
    private final ApiVersion apiVersion;

    ApiVersionCondition(ApiVersion apiVersion) {
        this.apiVersion = apiVersion;
    }

    public static void main(String[] args) {

        System.out.println(compareVersion("2.0", "2"));
        System.out.println(compareVersion("2.0", "2.0"));
        System.out.println(compareVersion("2.0", "2.0.0"));
        System.out.println("--------");
        System.out.println(compareVersion("2.0", "2.0.1"));
        System.out.println(compareVersion("2.0", "2.1"));
        System.out.println(compareVersion("2.0", "2.1.1"));
        System.out.println("--------");
        System.out.println(compareVersion("2.25", "2.1.0"));
        System.out.println(compareVersion("2.25", "2.2.0"));
        System.out.println(compareVersion("2.25", "2.04.0"));
        System.out.println(compareVersion("2.25", "2.5.0"));
        System.out.println(compareVersion("2.25", "2.15.0"));
        System.out.println(compareVersion("2.25", "2.35.0"));
        System.out.println("--------");
        System.out.println(compareVersion("2.25", "2.25.0.0"));
        System.out.println(compareVersion("2.25", "2.25.0.1"));
        System.out.println(compareVersion("2.25.0", "2.25.0.1"));

    }

    /**
     * [当class 和 method 请求url相同时，触发此方法用于合并url]
     * 官方解释：
     * - 某个接口有多个规则时，进行合并
     * - 比如类上指定了@RequestMapping的 url 为 root
     * - 而方法上指定的@RequestMapping的 url 为 method
     * - 那么在获取这个接口的 url 匹配规则时，类上扫描一次，方法上扫描一次，这个时候就需要把这两个合并成一个，表示这个接口匹配root/method
     *
     * @param other 相同api version condition
     * @return ApiVersionCondition
     */
    @Override
    public ApiVersionCondition combine(ApiVersionCondition other) {
        // 此处按优先级，method大于class
        return new ApiVersionCondition(other.getApiVersion());
    }

    /**
     * 判断是否成功，失败返回 null；否则，则返回匹配成功的条件
     *
     * @param httpServletRequest http request
     * @return 匹配成功条件
     */
    @Override
    public ApiVersionCondition getMatchingCondition(HttpServletRequest httpServletRequest) {
        String apiVersion = httpServletRequest.getHeader(CommonConstants.API_VERSION);
        if (StringUtils.isEmpty(apiVersion)) {
            return this;
        }
        if(!apiVersion.matches(VERSION_PREFIX_PATTERN)){
            return null;
        }
        // 通过uri匹配版本号
        if (compareVersion(apiVersion,this.getApiVersion().value())>=0) {
            return this;
        }
        return null;
    }

    /**
     * 多个都满足条件时，用来指定具体选择哪一个
     *
     * @param other              多个时
     * @param httpServletRequest http request
     * @return 取版本号最大的
     */
    @Override
    public int compareTo(ApiVersionCondition other, HttpServletRequest httpServletRequest) {
        // 当出现多个符合匹配条件的ApiVersionCondition，优先匹配版本号较大的
        //return other.getApiVersion().value().compareTo(getApiVersion().value()) >= 0 ? 1 : -1;
        return compareVersion(other.getApiVersion().value(),this.getApiVersion().value());
    }

    public ApiVersion getApiVersion() {
        return apiVersion;
    }

    /**
     * 版本号比较
     *
     * @param v1
     * @param v2
     * @return 0代表相等，1代表左边大，-1代表右边大
     * compareVersion("1.0.358","1.0.358")=1
     */
    public static int compareVersion(String v1, String v2) {
        if (v1.equals(v2)) {
            return 0;
        }
        String[] version1Array = v1.split("[.]");
        String[] version2Array = v2.split("[.]");
        int index = 0;
        int minLen = Math.min(version1Array.length, version2Array.length);
        long diff = 0;

        while (index < minLen
                && (diff = Long.parseLong(version1Array[index])
                - Long.parseLong(version2Array[index])) == 0) {
            index++;
        }
        if (diff == 0) {
            for (int i = index; i < version1Array.length; i++) {
                if (Long.parseLong(version1Array[i]) > 0) {
                    return 1;
                }
            }

            for (int i = index; i < version2Array.length; i++) {
                if (Long.parseLong(version2Array[i]) > 0) {
                    return -1;
                }
            }
            return 0;
        } else {
            return diff > 0 ? 1 : -1;
        }
    }
}
